//
//  PLTUserModel.m
//  RuntimeDemo
//
//  Created by zheng on 16/3/12.
//  Copyright © 2016年 zheng. All rights reserved.
//

#import "PLTUserModel.h"
#import <objc/runtime.h>
#import <objc/message.h>

@implementation PLTUserModel

- (instancetype)initWithDictionary:(NSDictionary *)dictionary {
    if (self = [super init]) {
        for (NSString *key in dictionary.allKeys) {
            id value = dictionary[key];
            
            if ([key isEqualToString:@"userModel"]) {
                PLTUserModel *userModel = [[PLTUserModel alloc] initWithDictionary:value];
                value = userModel;
                self.userModel = value;
                
                continue;
            }
            
            SEL setter = [self propertySetterWithKey:key];
            if (setter != nil) {
                ((void (*)(id, SEL, id))objc_msgSend)(self, setter, value);
            }
        }
    }
    return self;
}

- (NSDictionary *)toDictionary {
    unsigned int outCount = 0;
    objc_property_t *properties = class_copyPropertyList([self class], &outCount);
    
    if (outCount != 0) {
        NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithCapacity:outCount];
        
        for (int i = 0; i < outCount; i++) {
            objc_property_t property = properties[i];
            const char *propertyName = property_getName(property);
            NSString *key = [NSString stringWithUTF8String:propertyName];
            
            if ([key isEqualToString:@"description"] ||
                [key isEqualToString:@"debugDescription"] ||
                [key isEqualToString:@"hash"] ||
                [key isEqualToString:@"superclass"]) {
                continue;
            }
            
            SEL getter = [self propertGetterWithKey:key];
            if (getter) {
                // get the method's signature
                NSMethodSignature *signature = [self methodSignatureForSelector:getter];
                // through signature get NSInvocation instance
                NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:signature];
                [invocation setTarget:self];
                [invocation setSelector:getter];
                [invocation invoke];
                
                // receive return value
                __unsafe_unretained NSObject *propertyValue = nil;
                [invocation getReturnValue:&propertyValue];
                
                if (!propertyValue) {
                    if ([self respondsToSelector:@selector(defaultValueForEmptyProperty)]) {
                        NSDictionary *defaultValueDict = [self defaultValueForEmptyProperty];
                        id defaultValue = defaultValueDict[key];
                        propertyValue = defaultValue;
                    }
                } else {
                    [dict setObject:propertyValue forKey:key];
                }
            }
        }
        
        free(properties);
        return dict;
    }
    free(properties);
    return nil;
}

- (NSDictionary *)defaultValueForEmptyProperty {
    return @{@"name": [NSNull null],
             @"genders": [NSNull null],
             @"age": [NSNull null],
             @"address": [NSNull null]};
}

- (SEL)propertGetterWithKey:(NSString *)key {
    if (key) {
        SEL getter = NSSelectorFromString(key);
        if ([self respondsToSelector:getter]) {
            return getter;
        }
    }
    return nil;
}

- (SEL)propertySetterWithKey:(NSString *)key {
    NSString *propertySetter = key.capitalizedString;
    propertySetter = [NSString stringWithFormat:@"set%@:", propertySetter];
    
    SEL setter = NSSelectorFromString(propertySetter);
    if ([self respondsToSelector:setter]) {
        return setter;
    }
    
    return nil;
}

@end
